home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr26 / netprog.zip / NETPROG.TAR / tftp / netudp.c < prev    next >
C/C++ Source or Header  |  1989-12-17  |  12KB  |  468 lines

  1. /*
  2.  * TFTP network handling for UDP/IP connection.
  3.  */
  4.  
  5. #include    "netdefs.h"
  6.  
  7. #include    <netinet/in.h>
  8. #include    <arpa/inet.h>
  9. #include    <errno.h>
  10. extern int    errno;
  11.  
  12. #ifndef    CLIENT
  13. #ifndef    SERVER
  14. either CLIENT or SERVER must be defined
  15. #endif
  16. #endif
  17.  
  18. int    sockfd = -1;                /* fd for socket of server */
  19. char    openhost[MAXHOSTNAMELEN] = { 0 };    /* remember host's name */
  20.  
  21. extern int            traceflag;    /* TFTP variable */
  22.  
  23. #ifdef    CLIENT
  24.  
  25. extern struct sockaddr_in    udp_srv_addr;    /* set by udp_open() */
  26. extern struct servent        udp_serv_info;    /* set by udp_open() */
  27. static int            recv_first;
  28.  
  29.  
  30. /*
  31.  * Open the network connection.
  32.  */
  33.  
  34. int
  35. net_open(host, service, port)
  36. char    *host;        /* name of other system to communicate with */
  37. char    *service;    /* name of service being requested */
  38. int    port;        /* if > 0, use as port#, else use value for service */
  39. {
  40.     struct sockaddr_in    addr;
  41.  
  42.     /*
  43.      * Call udp_open() to create the socket.  We tell udp_open to
  44.      * not connect the socket, since we'll receive the first response
  45.      * from a port that's different from where we send our first
  46.      * datagram to.
  47.      */
  48.  
  49.     if ( (sockfd = udp_open(host, service, port, 1)) < 0)
  50.         return(-1);
  51.  
  52.     DEBUG2("net_open: host %s, port# %d",
  53.             inet_ntoa(udp_srv_addr.sin_addr),
  54.             ntohs(udp_srv_addr.sin_port));
  55.  
  56.     strcpy(openhost, host);        /* save the host's name */
  57.     recv_first = 1;            /* flag for net_recv() */
  58.  
  59.     return(0);
  60. }
  61.  
  62. /*
  63.  * Close the network connection.
  64.  */
  65.  
  66. net_close()
  67. {
  68.     DEBUG2("net_close: host = %s, fd = %d", openhost, sockfd);
  69.  
  70.     close(sockfd);
  71.  
  72.     sockfd = -1;
  73. }
  74.  
  75. /*
  76.  * Send a record to the other end.
  77.  * We use the sendto() system call, instead of send(), since the address
  78.  * of the server changes after the first packet is sent.
  79.  */
  80.  
  81. net_send(buff, len)
  82. char    *buff;
  83. int    len;
  84. {
  85.     register int    rc;
  86.  
  87.     DEBUG3("net_send: sent %d bytes to host %s, port# %d",
  88.             len, inet_ntoa(udp_srv_addr.sin_addr),
  89.             ntohs(udp_srv_addr.sin_port));
  90.  
  91.     rc = sendto(sockfd, buff, len, 0, (struct sockaddr *) &udp_srv_addr,
  92.                     sizeof(udp_srv_addr));
  93.     if (rc < 0)
  94.         err_dump("send error");
  95. }
  96.  
  97. /*
  98.  * Receive a record from the other end.
  99.  */
  100.  
  101. int                /* return #bytes in packet, or -1 on EINTR */
  102. net_recv(buff, maxlen)
  103. char    *buff;
  104. int    maxlen;
  105. {
  106.     register int        nbytes;
  107.     int            fromlen;    /* value-result parameter */
  108.     struct sockaddr_in    from_addr;    /* actual addr of sender */
  109.  
  110.     fromlen = sizeof(from_addr);
  111.     nbytes = recvfrom(sockfd, buff, maxlen, 0,
  112.                   (struct sockaddr *) &from_addr, &fromlen);
  113.     /*
  114.      * The recvfrom() system call can be interrupted by an alarm
  115.      * interrupt, in case it times out.  We just return -1 if the
  116.      * system call was interrupted, and the caller must determine
  117.      * if this is OK or not.
  118.      */
  119.  
  120.     if (nbytes < 0) {
  121.         if (errno == EINTR)
  122.             return(-1);
  123.         else
  124.             err_dump("recvfrom error");
  125.     }
  126.  
  127.     DEBUG3("net_recv: got %d bytes from host %s, port# %d",
  128.               nbytes, inet_ntoa(from_addr.sin_addr),
  129.               ntohs(from_addr.sin_port));
  130.  
  131.     /*
  132.      * The TFTP client using UDP/IP has a funny requirement.
  133.      * The problem is that UDP is being used for a
  134.      * "connection-oriented" protocol, which it wasn't really
  135.      * designed for.  Rather than tying up a single well-known
  136.      * port number, the server changes its port after receiving
  137.      * the first packet from a client.
  138.      *
  139.      * The first packet a client sends to the server (an RRQ or a WRQ)
  140.      * must be sent to its well-known port number (69 for TFTP).
  141.      * The server is then to choose some other port number for all
  142.      * subsequent transfers.  The recvfrom() call above will return
  143.      * the server's current address.  If the port number that we
  144.      * sent the last packet to (udp_srv_addr.sin_port) is still equal to
  145.      * the initial well-known port number (udp_serv_info.s_port), then we
  146.      * must set the server's port for our next transmission to be
  147.      * the port number from the recvfrom().  See Section 4 of the RFC
  148.      * where it talks about the TID (= port#).
  149.      *
  150.      * Furthermore, after we have determined the port number that
  151.      * we'll be receiving from, we can verify each datagram to make
  152.      * certain its from the right place.
  153.      */
  154.  
  155.     if (recv_first) {
  156.         /*
  157.          * This is the first received message.
  158.          * The server's port should have changed.
  159.          */
  160.  
  161.         if (udp_srv_addr.sin_port == from_addr.sin_port)
  162.             err_dump("first receive from port %d",
  163.                      ntohs(from_addr.sin_port));
  164.  
  165.         udp_srv_addr.sin_port = from_addr.sin_port;
  166.                 /* save the new port# of the server */
  167.         recv_first = 0;
  168.  
  169.     } else if (udp_srv_addr.sin_port != from_addr.sin_port) {
  170.         err_dump("received from port %d, expected from port %d",
  171.                 ntohs(from_addr.sin_port),
  172.                 ntohs(udp_srv_addr.sin_port));
  173.     }
  174.  
  175.     return(nbytes);        /* return the actual length of the message */
  176. }
  177.  
  178. #endif    /* CLIENT */
  179.  
  180. #ifdef    SERVER
  181.  
  182. #include    <sys/ioctl.h>
  183.  
  184. struct sockaddr_in    udp_srv_addr;    /* server's Internet socket addr */
  185. struct sockaddr_in    udp_cli_addr;    /* client's Internet socket addr */
  186. struct servent        udp_serv_info;    /* from getservbyname() */
  187.  
  188. static int    recv_nbytes = -1;
  189. static int    recv_first = 0;
  190.  
  191. extern char    recvbuff[];        /* this is declared in initvars.c */
  192.  
  193. /*
  194.  * Initialize the network connection for the server, when it has *not*
  195.  * been invoked by inetd.
  196.  */
  197.  
  198. net_init(service, port)
  199. char    *service;    /* the name of the service we provide */
  200. int    port;        /* if nonzero, this is the port to listen on;
  201.                overrides the standard port for the service */
  202. {
  203.     struct servent    *sp;
  204.  
  205.     /*
  206.      * We weren't started by a master daemon.
  207.      * We have to create a socket ourselves and bind our well-known
  208.      * address to it.
  209.      */
  210.  
  211.     if ( (sp = getservbyname(service, "udp")) == NULL)
  212.         err_dump("net_init: unknown service: %s/udp", service);
  213.  
  214.     if (port > 0)
  215.         sp->s_port = htons(port);    /* caller's value */
  216.     udp_serv_info = *sp;            /* structure copy */
  217.  
  218.     if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  219.         err_dump("net_init: can't create datagram socket");
  220.  
  221.     /*
  222.      * Bind our local address so that any client can send to us.
  223.      */
  224.  
  225.     bzero((char *) &udp_srv_addr, sizeof(udp_srv_addr));
  226.     udp_srv_addr.sin_family      = AF_INET;
  227.     udp_srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  228.     udp_srv_addr.sin_port = sp->s_port;
  229.  
  230.     if (bind(sockfd, (struct sockaddr *) &udp_srv_addr,
  231.                     sizeof(udp_srv_addr)) < 0)
  232.         err_dump("net_init: can't bind local address");
  233. }
  234.  
  235. /*
  236.  * Initiate the server's end.
  237.  * We are passed a flag that says whether or not we were started
  238.  * by a "master daemon," such as the inetd program under 4.3BSD.
  239.  * A master daemon will have already waited for a message to arrive
  240.  * for us, and will have already set up the connection to the client.
  241.  * If we weren't started by a master daemon, then we must wait for a
  242.  * client's request to arrive.
  243.  */
  244.  
  245. int
  246. net_open(inetdflag)
  247. int    inetdflag;    /* true if inetd started us */
  248. {
  249.     register int    childpid, nbytes;
  250.     int        on;
  251.  
  252.     on = 1;
  253.  
  254.     if (inetdflag) {
  255. #ifdef    BSD        /* assumes 4.3BSD inetd */
  256.         /*
  257.          * When we're fired up by inetd under 4.3BSD, file
  258.          * descriptors 0, 1 and 2 are sockets to the client.
  259.          * We want to first receive the message that's waiting
  260.          * for us on the socket, and then close the socket.
  261.          * This will let inetd go back to waiting for another
  262.          * request on our "well-known port."
  263.          */
  264.  
  265.         sockfd = 0;    /* descriptor for net_recv() to recvfrom() */
  266.  
  267.         /*
  268.          * Set the socket to nonblocking, since inetd won't invoke
  269.          * us unless there's a datagram ready for us to read.
  270.          */
  271.  
  272.         if (ioctl(sockfd, FIONBIO, (char *) &on) < 0)
  273.             err_dump("ioctl FIONBIO error");
  274.  
  275. #endif    /* BSD inetd specifics */
  276.  
  277.     }
  278.  
  279.     /*
  280.      * Now read the first message from the client.
  281.      * In the inetd case, the message is already here and the call to
  282.      * net_recv() returns immediately.  In the other case, net_recv()
  283.      * blocks until a client request arrives.
  284.      */
  285.  
  286.     recv_first  =  1;    /* tell net_recv to save the address */
  287.     recv_nbytes = -1;    /* tell net_recv to do the actual read */
  288.     nbytes = net_recv(recvbuff, MAXBUFF);
  289.  
  290.     /*
  291.      * Fork a child process to handle the client's request.
  292.      * In the inetd case, the parent exits, which allows inetd to
  293.      * handle the next request that arrives to this well-known port
  294.      * (inetd's wait mode for a datagram socket).
  295.      * Otherwise the parent returns the child pid to the caller, which
  296.      * is probably a concurrent server that'll call us again, to wait
  297.      * for the next client request to this well-known port.
  298.      */
  299.  
  300.     if ( (childpid = fork()) < 0)
  301.         err_dump("server can't fork");
  302.  
  303.     else if (childpid > 0) {    /* parent */
  304.         if (inetdflag)
  305.             exit(0);        /* inetd case; we're done */
  306.         else
  307.             return(childpid);    /* independent server */
  308.     }
  309.  
  310.     /*
  311.      * Child process continues here.
  312.      * First close the socket that is bound to the well-known address:
  313.      * the parent will handle any further requests that arrive there.
  314.      * We've already read the message that arrived for us to handle.
  315.      */
  316.  
  317.     if (inetdflag) {
  318.         close(0);
  319.         close(1);
  320.         close(2);
  321.     } else {
  322.         close(sockfd);
  323.     }
  324.     errno = 0;        /* in case it was set by a close() */
  325.  
  326.     /*
  327.      * Create a new socket.
  328.      * Bind any local port# to the socket as our local address.
  329.      * We don't connect(), since net_send() uses the sendto()
  330.      * system call, specifying the destination address each time.
  331.      */
  332.  
  333.     if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  334.         err_dump("net_open: can't create socket");
  335.  
  336.     bzero((char *) &udp_srv_addr, sizeof(udp_srv_addr));
  337.     udp_srv_addr.sin_family      = AF_INET;
  338.     udp_srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  339.     udp_srv_addr.sin_port        = htons(0);
  340.     if (bind(sockfd, (char *) &udp_srv_addr, sizeof(udp_srv_addr)) < 0)
  341.         err_dump("net_open: bind error");
  342.  
  343.     /*
  344.      * Now we'll set a special flag for net_recv(), so that
  345.      * the next time it's called, it'll know that the recvbuff[]
  346.      * already has the received packet in it (from our call to
  347.      * net_recv() above).
  348.      */
  349.  
  350.     recv_nbytes = nbytes;
  351.  
  352.     return(0);
  353. }
  354.  
  355. /*
  356.  * Close a socket.
  357.  */
  358.  
  359. net_close()
  360. {
  361.     DEBUG2("net_close: host = %s, fd = %d", openhost, sockfd);
  362.  
  363.     close(sockfd);
  364.  
  365.     sockfd = -1;
  366. }
  367.  
  368. /*
  369.  * Send a record to the other end.
  370.  * The "struct sockaddr_in cli_addr" specifies the client's address.
  371.  */
  372.  
  373. net_send(buff, len)
  374. char    *buff;
  375. int    len;
  376. {
  377.     register int    rc;
  378.  
  379.     DEBUG3("net_send: sent %d bytes to host %s, port# %d",
  380.             len, inet_ntoa(udp_cli_addr.sin_addr),
  381.             ntohs(udp_cli_addr.sin_port));
  382.  
  383.     rc = sendto(sockfd, buff, len, 0, (struct sockaddr *) &udp_cli_addr,
  384.                 sizeof(udp_cli_addr));
  385.     if (rc != len)
  386.         err_dump("sendto error");
  387. }
  388.  
  389. /*
  390.  * Receive a record from the other end.
  391.  * We're called not only by the user, but also by net_open() above,
  392.  * to read the first datagram after a "connection" is established.
  393.  */
  394.  
  395. int
  396. net_recv(buff, maxlen)
  397. char    *buff;
  398. int    maxlen;
  399. {
  400.     register int        nbytes;
  401.     int            fromlen;    /* value-result parameter */
  402.     extern int        tout_flag;    /* set by SIGALRM */
  403.     struct sockaddr_in    from_addr;
  404.  
  405.     if (recv_nbytes >= 0) {
  406.         /*
  407.          * First message has been handled specially by net_open().
  408.          * It's already been read into recvbuff[].
  409.          */
  410.  
  411.         nbytes = recv_nbytes;
  412.         recv_nbytes = -1;
  413.         return(nbytes);
  414.     }
  415.  
  416. again:
  417.     fromlen = sizeof(from_addr);
  418.     nbytes = recvfrom(sockfd, buff, maxlen, 0,
  419.                 (struct sockaddr *) &from_addr, &fromlen);
  420.     /*
  421.      * The server can have its recvfrom() interrupted by either an
  422.      * alarm timeout or by a SIGCLD interrupt.  If it's a timeout,
  423.      * "tout_flag" will be set and we have to return to the caller
  424.      * to let them determine if another receive should be initiated.
  425.      * For a SIGCLD signal, we can restart the recvfrom() ourself.
  426.      */
  427.  
  428.     if (nbytes < 0) {
  429.         if (errno == EINTR) {
  430.             if (tout_flag)
  431.                 return(-1);
  432.  
  433.             errno = 0;    /* assume SIGCLD */
  434.             goto again;
  435.         }
  436.         err_dump("recvfrom error");
  437.     }
  438.  
  439.     DEBUG3("net_recv: got %d bytes from host %s, port# %d",
  440.             nbytes, inet_ntoa(from_addr.sin_addr),
  441.             ntohs(from_addr.sin_port));
  442.  
  443.     /*
  444.      * If "recv_first" is set, then we must save the received
  445.      * address that recvfrom() stored in "from_addr" in the
  446.      * global "udp_cli_addr".
  447.      */
  448.  
  449.     if (recv_first) {
  450.         bcopy((char *) &from_addr, (char *) &udp_cli_addr,
  451.                         sizeof(from_addr));
  452.         recv_first = 0;
  453.     }
  454.  
  455.     /*
  456.      * Make sure the message is from the expected client.
  457.      */
  458.  
  459.     if (udp_cli_addr.sin_port != 0 &&
  460.         udp_cli_addr.sin_port != from_addr.sin_port)
  461.         err_dump("received from port %d, expected from port %d",
  462.                ntohs(from_addr.sin_port), ntohs(udp_cli_addr.sin_port));
  463.  
  464.     return(nbytes);        /* return the actual length of the message */
  465. }
  466.  
  467. #endif    /* SERVER */
  468.